﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Lucene.Net.Index;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using AteneoLibriBusinessLogic.Entities;
using Lucene.Net.Documents;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using AteneoLibri.DomainObject;
using Lucene.Net.Store;

namespace AteneoLibri.Utility.Lucene_NET
{
    public class LuceneFacade
    {

        private static Analyzer ANALYZER = new StandardAnalyzer();
        private static string INDEX_PATH = HttpContext.Current.ApplicationInstance.Server.MapPath(System.Configuration.ConfigurationManager.AppSettings["LuceneIndexPath"]);
        private static List<BookResults> results = null;

        private static IndexWriter GetIndex(string indexPathLocation)
        {
            Directory dir = FSDirectory.Open(new System.IO.DirectoryInfo(@indexPathLocation));
            IndexWriter indexWriter = new IndexWriter(dir, ANALYZER, false);
            return indexWriter;
        }

        public static void AddBookToIndex(Book book)
        {
            IndexWriter writer = GetIndex(INDEX_PATH);
            
            // create a document
            Lucene.Net.Documents.Document doc = new Lucene.Net.Documents.Document();

            doc.Add(new Field("IdBook", book.IdBook.ToString(), Field.Store.YES, Field.Index.NO));
            doc.Add(new Field("Title", book.Title.ToString(), Field.Store.YES, Field.Index.ANALYZED));
            doc.Add(new Field("Author", book.Author.ToString(), Field.Store.YES, Field.Index.ANALYZED));

            writer.AddDocument(doc);
            
            writer.Optimize();
            writer.Flush();
            writer.Close();
        }

        public static void RemoveBookFromIndex(Book book)
        {

        }

        public static void UpdateBook(Book book)
        {
            // Rimuove il documento indicizzato e rimette quello nuovo
        }

        public static List<Book> Search(string userQuery)
        {
            //QueryParser parser = new QueryParser("Title", ANALYZER);
            MultiFieldQueryParser parser = new MultiFieldQueryParser(new string[] { "Title", "Author" }, ANALYZER);
            Query query = parser.Parse(userQuery);

            Term term = new Term("Title", userQuery);
            Query fuzzy = new FuzzyQuery(term);
            Query wildcard = new WildcardQuery(term);

            IndexSearcher searcher = new IndexSearcher(INDEX_PATH);

            results = new List<BookResults>();

            Hits hits = searcher.Search(query);
            Hits fuzzyHits = searcher.Search(fuzzy);
            Hits wildcardHits = searcher.Search(wildcard);

            //TopDocs topDocs = searcher.Search(query, 20);

            //List<BookResults> searchResults = new List<BookResults>();

            //searchResults.AddRange(ProcessQueryResults(hits));
            //searchResults.AddRange(ProcessQueryResults(fuzzyHits));
            //searchResults.AddRange(ProcessQueryResults(wildcardHits));

            ProcessQueryResults(hits, 20);
            ProcessQueryResults(fuzzyHits, 10);
            ProcessQueryResults(wildcardHits, 5);

            searcher.Close();

            results = results.Distinct().OrderByDescending(x => x.Rank).ToList();

            return results.Select(x => x.Book).ToList();
        }

        private static void ProcessQueryResults(Hits hits, float boost)
        {            
            for (int i = 0; i < hits.Length(); i++)
            {
                Document doc = hits.Doc(i);
                float score = hits.Score(i) * boost;

                bool isSold = false;
                var bookId = doc.Get("IdBook");
                Book book = null;
                using (wsAteneoLibri.AteneoLibriServiceClient service = new wsAteneoLibri.AteneoLibriServiceClient())
                {
                    try
                    {
                        book = service.getBookById(new Guid(bookId));
                        isSold = service.getSalesByBookId(book.IdBook).Where(x => x.EndDate != null).Count() > 0;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
                if (book != null && !isSold && (!book.IsDeleted.HasValue || (book.IsDeleted.HasValue && book.IsDeleted == false))) // Aggiunta verifica se libro è stato cancellato
                {
                    BookResults bookResult = null;

                    BookResults discoveredItem = results.Where(x => x.Book.IdBook == book.IdBook).FirstOrDefault(); // il libro è già presente nella lista

                    if (discoveredItem != null) // se l'elemento esiste aggiorno lo score (aumento il rank)
                    {
                        double newScore = discoveredItem.Rank + score;
                        results.Remove(discoveredItem);
                        
                        bookResult = new BookResults(newScore, book);
                    }
                    else // se non esiste lo creo
                    {
                        bookResult = new BookResults(Convert.ToDouble(score), book);
                    }

                    results.Add(bookResult);
                }
                
            }

        }
    }
}